// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PADDLE_TTS_SERVING_FRONT_FRONT_INTERFACE_H
#define PADDLE_TTS_SERVING_FRONT_FRONT_INTERFACE_H

#include <fstream>
#include <map>
#include <memory>
#include <string>
#include <algorithm>
#include "absl2/strings/str_split.h"
#include "front/text_normalize.h"
#include "cppjieba/Jieba.hpp"
#include "sql/sql_api.h"


#define DatabaseDictTableZhChar		"Zh_Char"
#define DatabaseDictTableZhPhrase	"Zh_Phrase"
#define DatabaseDictTableEnWord		"En_Word"

#define DatabaseDictTableZhSymbol	"Zh_Symbol"
#define DatabaseDictTableEnSymbol	"En_Symbol"

#define DatabaseDictTableZhTrand	"Zh_Trand"

using namespace std;

namespace ppspeech
{
	class FrontEngineInterface : public TextNormalizer
	{
		//=========== 接口 ============
	public:
		explicit FrontEngineInterface(const string fInDictFolderPath, const string fInUserContextFilepath, const bool fInUpdateBiasfileFlag);
		bool FrontEngineLoadPhonemeMap(const string fInModelPhoneidxFilepath);
		~FrontEngineInterface();
		
		bool FrontGetInitialedFlag();

	private:
		bool mFrontInitialedFlag;
		cppjieba::Jieba* mFrontJieba;


		//=========== 字典 ============
	private:
		bool FrontSetDictMap(const string& fInDictFilePath, const string fInSplitSymbol, map<string, string>* fOutDictMap);		//生成字典
		bool FrontPinyinMap(const string& fInDictFilePath, map<string, pair<string,string> >* fOutDictMap);

	private:
		map<string, pair<string,string> > mPhonemeIndexMap;	//音素映射表

		const string mDbDictPath;				//"../dict/phoneme/phoneme.db"
		ReadDictDatabase mDictionary;

		const string mDbBiasDictPath;			//"../dict/phoneme/phonemeBias.db"
		ReadDictDatabase mDictionaryBias;

		//更新热词词库
		void FrontUpdateBiasFile(const string fInContextFilepath, const string fInDictionaryFolderpath);

		bool FrontContext_AddChineseContext(const string& fpInKey, const string& fpInValue, WriteDictDatabase& fpOutWrBiasDictDb, ofstream& fpOutJiebaUser);

		bool FrontContext_AddEnglishContext(const string& fpInKey, const string& fpInValue, WriteDictDatabase& fpOutWrBiasDictDb);

		//=========== 音素 ============
	public:
		bool FrontGetSentencePhoneme(const string& fpInSentence, vector< vector<string> >* fpOutPhonemes);		//生成句子的音素id

	private:
		//根据分词结果获取词的音素，音调id，并对读音进行适当修改(Modify Tone)。
		bool FrontGetCutWordsIds(const vector<pair<string, string>>& fpInCutResult, vector< vector<string> >* fpOutPhonemes);

		//结巴分词生成包含词和词性的分词结果，再对分词结果进行适当修改(MergeforModify)
		bool FrontCutSentenceWords(const string& sentence, vector<pair<string, string>>& cut_result);



	public:

		int TraditionalToSimplified(const wstring &sentence, wstring *sentence_simp);			// 繁体转简体

		vector<string> FrontUtf8WordSplit(const string& str);		// utf8字符串拆分

		// 由 词+词性的分词结果转为仅包含词的结果
		int GetSegResult(vector<pair<string, string>>& seg, vector<string>* seg_words);

		// 根据韵母判断该词中每个字的读音都为第三声。true表示词中每个字都是第三声
		bool AllToneThree(const vector<string> &finals);

		// 判断词是否是叠词
		bool IsReduplication(const string &word);

		// 获取每个字词的声母韵母列表
		int GetInitialsFinals(const string &word,
							vector<string> *word_initials,
							vector<string> *word_finals);

		// 获取每个字词的韵母列表
		int GetFinals(const string &word,
					vector<string> *word_finals);

		// 整个词转成向量形式，向量的每个元素对应词的一个字
		int Word2WordVec(const string &word,
						vector<wstring> *wordvec);

		// 将整个词重新进行 full cut，分词后，各个词会在词典中
		int SplitWord(const string &word,
					vector<string> *fullcut_word);

		// 对分词结果进行处理：对包含“不”字的分词结果进行整理
		vector<pair<string, string>> MergeBu(
			vector<pair<string, string>>& seg_result);

		// 对分词结果进行处理：对包含“一”字的分词结果进行整理
		vector<pair<string, string>> Mergeyi(
			vector<pair<string, string>>& seg_result);

		// 对分词结果进行处理：对前后相同的两个字进行合并
		vector<pair<string, string>> MergeReduplication(
			vector<pair<string, string>>& seg_result);

		// 对一个词和后一个词他们的读音均为第三声的两个词进行合并
		vector<pair<string, string>> MergeThreeTones(vector<pair<string, string>>& seg_result);

		// 对一个词的最后一个读音和后一个词的第一个读音为第三声的两个词进行合并
		vector<pair<string, string>> MergeThreeTones2(vector<pair<string, string>>& seg_result);

		// 对分词结果进行处理：对包含“儿”字的分词结果进行整理
		vector<pair<string, string>> MergeEr(vector<pair<string, string>>& seg_result);



		// 对分词结果进行处理、修改
		int MergeforModify(vector<pair<string, string>>& seg_result, vector<pair<string, string>>& merge_seg_result);

		void FrontMergeEnglishThrow1(vector<pair<string, string>>& fvInParticiple);
		void FrontMergeEnglishThrow2(vector<pair<string, string>>& fvInParticiple);


		// 对包含“不”字的相关词音调进行修改
		bool FrontModifyBuSandi(const string& fcaInWord, vector<string>& faIOFinals);

		// 对包含“一”字的相关词音调进行修改
		bool FrontModifyYiSandhi(const string& fcaInWord, vector<string>& faIOFinals);

		// 对一些特殊词（包括量词，语助词等）的相关词音调进行修改
		bool FrontModifyNeuralSandhi(const string& fcaInWord, const string& fcaInPos, vector<string>& faIOFinals);

		// 对包含第三声的相关词音调进行修改
		bool FrontModifyThreeSandhi(const string& fcaInWord, vector<string>& faIOFinals);

		// 对字词音调进行处理、修改
		bool FrontModifyTone(const string& fcaInWord, const string& fcaInPos, vector<string>& faIOFinals);


		// 对儿化音进行处理
		vector<vector<string>> MergeErhua(
			const vector<string> &initials,
			const vector<string> &finals,
			const string &word,
			const string &pos);


	private:
		//======================================================================================

		const vector<string> mcPunctuation = {"，", "。", "、", "？", "：", "；", "~", "！", ",", ".", "?", "!", ":", ";", "/", "\\"};
		const vector<string> _punc_omit = {"“", "”", "\"", "\""};

		// 需要儿化音处理的词语
		const vector<string> must_erhua = {"小院儿", "胡同儿", "范儿", "老汉儿", "撒欢儿", "寻老礼儿", "妥妥儿"};
		const vector<string> not_erhua = {"虐儿",   "为儿",   "护儿",   "瞒儿",   "救儿",   "替儿",
									"有儿",   "一儿",   "我儿",   "俺儿",   "妻儿",   "拐儿",
									"聋儿",   "乞儿",   "患儿",   "幼儿",   "孤儿",   "婴儿",
									"婴幼儿", "连体儿", "脑瘫儿", "流浪儿", "体弱儿", "混血儿",
									"蜜雪儿", "舫儿",   "祖儿",   "美儿",   "应采儿", "可儿",
									"侄儿",   "孙儿",   "侄孙儿", "女儿",   "男儿",   "红孩儿",
									"花儿",   "虫儿",   "马儿",   "鸟儿",   "猪儿",   "猫儿",
									"狗儿"};


		const vector<string> must_not_neural_tone_words = {"男子", "女子", "分子", "原子", "量子", "莲子", "石子", "瓜子", "电子"};
		// 需要轻声处理的词语
		const vector<string> must_neural_tone_words = {
			"麻烦", "麻利", "鸳鸯", "高粱", "骨头", "骆驼", "马虎", "首饰", "馒头",
			"馄饨", "风筝", "难为", "阔气", "闺女", "门道", "锄头", "铺盖", //"队伍",
			"铃铛", "铁匠", "钥匙", "里脊", "里头", "部分", "那么", "道士", "造化",
			"迷糊", "连累", "这么", "这个", "运气", "过去", "软和", "转悠", "踏实",
			"跳蚤", "跟头", "趔趄", "财主", "豆腐", "讲究", "记性", "记号", "认识",
			"规矩", "见识", "裁缝", "补丁", "衣裳", "衣服", "衙门", "街坊", "行李",
			"行当", "蛤蟆", "蘑菇", "薄荷", "葫芦", "葡萄", "萝卜", "荸荠", "苗条",
			"苗头", "苍蝇", "芝麻", "舒服", "舌头", "自在", "膏药", "脾气",	//"舒坦", 
			"脑袋", "脊梁", "能耐", "胳膊", "胭脂", "胡萝", "胡琴", "胡同", "聪明",
			"耽误", "耽搁", "耷拉", "耳朵", "老爷", "老实", "老婆", "老头", "老太",
			"翻腾", "罗嗦", "罐头", "编辑", "结实", "红火", "累赘", "糨糊", "糊涂",
			"精神", "粮食", "簸箕", "篱笆", "算计", "算盘", "答应", "笤帚", "笑语",
			"笑话", "窟窿", "窝囊", "窗户", "稳当", "稀罕", "称呼", "秧歌", "秀气",
			"秀才", "福气", "祖宗", "砚台", "码头", "石榴", "石头", "石匠", "知识",
			"眼睛", "眯缝", "眨巴", "眉毛", "盘算", "白净", "痢疾", "痛快", //"相声", 
			"疟疾", "疙瘩", "疏忽", "畜生", "生意", "甘蔗", "琵琶", "琢磨", "琉璃",
			"玻璃", "玫瑰", "玄乎", "狐狸", "状元", "特务", "牲口", "牙碜", "牌楼",
			"爽快", "爱人", "热闹", "烧饼", "烟筒", "烂糊", "点心", "炊帚", "灯笼",
			"火候", "漂亮", "滑溜", "溜达", "温和", "清楚", "消息", "浪头", "活泼",
			"比方", "正经", "欺负", "模糊", "槟榔", "棺材", "棒槌", "棉花", "核桃",
			"栅栏", "柴火", "架势", "枕头", "枇杷", "机灵", "本事", "木头", "木匠",
			"朋友", "月饼", "月亮", "暖和", "明白", "时候", "新鲜", "故事", "收拾",
			"收成", "提防", "挖苦", "挑剔", "指甲", "指头", "拾掇", "拳头", "拨弄",
			"招牌", "招呼", "抬举", "护士", "折腾", "扫帚", "打量", "打算", "打点",
			"打扮", "打听", "打发", "扎实", "扁担", "戒指", "懒得", "意识", "意思",
			"情形", "悟性", "怪物", "思量", "怎么", "念头", "念叨", "快活", "忙活",
			"志气", "心思", "得罪", "张罗", "弟兄", "开通", "应酬", "庄稼", "干事",
			"帮手", "帐篷", "希罕", "师父", "师傅", "巴结", "巴掌", "差事", "工夫",
			"岁数", "屁股", "尾巴", "少爷", "小气", "小伙", "将就", "对头", "对付",
			"寡妇", "家伙", "客气", "实在", "官司", "学问", "字号", "嫁妆",	//"学生", 
			"媳妇", "媒人", "婆家", "娘家", "委屈", "姑娘", "姐夫", "妯娌", "妥当",
			"妖精", "奴才", "女婿", "头发", "太阳", "大爷", "大方", "大意", "大夫",
			"多少", "多么", "外甥", "壮实", "地道", "地方", "在乎", "困难", "嘴巴",
			"嘱咐", "嘟囔", "嘀咕", "喇嘛", "喇叭", "商量", "唾沫", "哑巴",	//"喜欢", 
			"哈欠", "哆嗦", "咳嗽", "和尚", "告诉", "告示", "含糊", "吓唬", "后头",
			"名字", "名堂", "合同", "吆喝", "叫唤", "口袋", "厚道", "厉害", "千斤",
			"包袱", "包涵", "匀称", "勤快", "动静", "动弹", "功夫", "力气", "前头",
			"刺猬", "刺激", "别扭", "利落", "利索", "利害", "分析", "出息", "凑合",
			"凉快", "冷战", "冤枉", "冒失", "养活", "关系", "先生", "兄弟", "便宜",
			"使唤", "佩服", "作坊", "体面", "位置", "似的", "伙计", "休息", "什么",
			"人家", "亲戚", "亲家", "交情", "云彩", "事情", "买卖", "主意", "丫头",
			"丧气", "两口", "东西", "东家", "世故", "不由", "不在", "下水", "下巴",
			"上头", "上司", "丈夫", "丈人", "一辈", "那个", "菩萨", "父亲", "母亲",
			"咕噜", "邋遢", "费用", "冤家", "甜头", "荒唐", "大人", "泥鳅", //"介绍",
			"幸福", "熟悉", "计划", "扑腾", "蜡烛", "姥爷", "照顾", "喉咙", "吉他",
			"弄堂", "蚂蚱", "凤凰", "拖沓", "寒碜", "糟蹋", "倒腾", "报复", "逻辑",
			"盘缠", "喽啰", "牢骚", "咖喱", "扫把", "惦记"};

		const vector<string> mcLetterPhoneme = {
			"EY1",		"B IY1",	"S IY1",	"D IY1",	"IY1",		"EH1 F",	"JH IY1",
			"EY1 CH",	"AY1",		"JH EY1",	"K EY1",	"EH1 L",	"EH1 M",	"EH1 N",
			"OW1",		"P IY1",	"K Y UW1",				"AA1 R",	"EH1 S",	"T IY1",
			"Y UW1",	"V IY1",	"D AH1 B AH0 L Y UW0",	"EH1 K S",	"W AY1",	"Z EH1 D"	//"Z IY1"
		};

		//英语辅音
		const vector<string> mcMapStrBool_EnPausePhoneme = {
			"T", "K",
			//"P", "T", "K", "F", "TH", "S", "SH", "HH", "CH", "M", "N", "NG",
			//"B", "D", "G", "V", "DH", "Z", "ZH", "R", "JH", "Y", "W", "L",
		};

	};
}  // namespace ppspeech
#endif